package adventure.room;

import adventure.placeable.Fightable;
import adventure.placeable.Item;
import adventure.placeable.Mobile;
import adventure.placeable.trigger.Trigger;
import colour.ColourUtil;
import java.util.ArrayList;
import player.Player;

/**
 *
 * @author Michael Hanns
 *
 */
public class RoomManagerImp implements RoomManager, RoomManagerEdit, Cloneable {

    private Room[][][] rooms;
    public static final int maxX = 100;
    public static final int maxY = 100;
    public static final int maxZ = 100;
    private int sx;
    private int sy;
    private int sz;

    public RoomManagerImp() {
        sx = 0;
        sy = 0;
        sz = 0;
        rooms = new Room[maxX][maxY][maxZ];
    }

    @Override
    public void setStartArea(int x, int y, int z) {
        sx = x;
        sy = y;
        sz = z;
    }

    @Override
    public int sx() {
        return sx;
    }

    @Override
    public int sy() {
        return sy;
    }

    @Override
    public int sz() {
        return sz;
    }

    @Override
    public boolean roomExists(int x, int y, int z) {
        if (x >= 0 && y >= 0 && z >= 0
                && x < maxX && y < maxY && z < maxZ && rooms[x][y][z] != null) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean roomRepops(int x, int y, int z) {
        if (roomExists(x, y, z)) {
            return rooms[x][y][z].repops();
        } else {
            return false;
        }
    }

    @Override
    public boolean tickPassed(int x, int y, int z) {
        if (roomExists(x, y, z)) {
            return rooms[x][y][z].tickPassed();
        } else {
            return false;
        }
    }

    @Override
    public int getTicksUntilRepop(int x, int y, int z) {
        if (roomExists(x, y, z)) {
            return rooms[x][y][z].getTicksUntilRepop();
        } else {
            return 0;
        }
    }

    @Override
    public String getRoomTitle(int x, int y, int z) {
        return rooms[x][y][z].getTitle();
    }

    @Override
    public String getRoomLitTitle(int x, int y, int z) {
        return rooms[x][y][z].getLitTitle();
    }

    @Override
    public String getRoomDescription(int x, int y, int z) {
        return rooms[x][y][z].getDescription();
    }

    @Override
    public String getRoomLitDescription(int x, int y, int z) {
        return rooms[x][y][z].getLitDescription();
    }

    @Override
    public String getRoomYouSee(int x, int y, int z, int playerID) {
        return rooms[x][y][z].getYouSee(playerID);
    }

    @Override
    public int getSpawnLevel(int x, int y, int z) {
        return rooms[x][y][z].getSpawnLevel();
    }

    @Override
    public int getCurrencyContents(int x, int y, int z) {
        return rooms[x][y][z].getCurrencyContents();
    }

    @Override
    public boolean getRoomLowLight(int x, int y, int z) {
        return rooms[x][y][z].isLowLight();
    }

    @Override
    public String getPlaceableDescription(int x, int y, int z, String obj) {
        if (rooms[x][y][z].lightPresent()) {
            if (rooms[x][y][z].getItem(obj).getID() > 0) {
                return rooms[x][y][z].getItem(obj).getDescription();
            } else if (rooms[x][y][z].getMobile(obj).getID() > 0) {
                String desc = rooms[x][y][z].getMobile(obj).getDescription();
                if (!rooms[x][y][z].getMobile(obj).isDead()) {
                    desc += "\n" + rooms[x][y][z].getMobile(obj).getHealthStatus();
                }
                return desc;
            } else if (rooms[x][y][z].getTrigger(obj).getID() > 0) {
                return rooms[x][y][z].getTrigger(obj).getDescription();
            } else if (rooms[x][y][z].getPlayer(obj).getID() > 0) {
                String desc = rooms[x][y][z].getPlayer(obj).getDescription();
                if (!rooms[x][y][z].getPlayer(obj).isDead()) {
                    desc += "\n" + rooms[x][y][z].getPlayer(obj).getHealthStatus();
                }
                return desc;
            } else {
                return "You do not see any " + obj + " here!";
            }
        } else {
            return "You cannot make anything out in the darkness.";
        }
    }

    @Override
    public int getItemIDAtPosition(int x, int y, int z, int pos) {
        return rooms[x][y][z].getItemIDAtPosition(pos);
    }

    @Override
    public int getMobileIDAtPosition(int x, int y, int z, int pos) {
        return rooms[x][y][z].getMobileIDAtPosition(pos);
    }

    @Override
    public int getTriggerIDAtPosition(int x, int y, int z, int pos) {
        return rooms[x][y][z].getTriggerIDAtPosition(pos);
    }

    @Override
    public boolean newRoom(int x, int y, int z) {
        if (rooms[x][y][z] == null) {
            rooms[x][y][z] = new RoomImp(x, y, z);

            if (rooms[sx][sy][sz] == null) {
                sx = x;
                sy = y;
                sz = z;
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean newRoom(RoomEdit r, int x, int y, int z) {
        if (rooms[x][y][z] == null) {
            rooms[x][y][z] = r;
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean copyRoom(int sx, int sy, int sz, int dx, int dy, int dz) {
        if (rooms[dx][dy][dz] == null && rooms[sx][sy][sz] != null) {
            rooms[dx][dy][dz] = rooms[sx][sy][sz].cloneThis();
            ((RoomEdit) rooms[dx][dy][dz]).setXYZ(dx, dy, dz);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean editRoom(int x, int y, int z, String field, String val) {
        val = val.replace("&", "and");
        if (rooms[x][y][z] != null) {
            if (field.equalsIgnoreCase("TITLE")) {
                ((RoomEdit) rooms[x][y][z]).setTitle(val);
            } else if (field.equalsIgnoreCase("DESCRIPTION")) {
                ((RoomEdit) rooms[x][y][z]).setDescription(val);
            } else if (field.equalsIgnoreCase("CLOSED")) {
                val = val.toUpperCase();
                for (int c = 0; c < val.length(); c++) {
                    if (val.charAt(c) != 'N' && val.charAt(c) != 'E'
                            && val.charAt(c) != 'S' && val.charAt(c) != 'W'
                            && val.charAt(c) != 'U' && val.charAt(c) != 'D') {
                        val = val.replace(val.charAt(c) + "", "");
                        c = -1;
                    }
                }
                ((RoomEdit) rooms[x][y][z]).setClosed(val);
            } else if (field.equalsIgnoreCase("LOCKED")) {
                val = val.toUpperCase();
                for (int c = 0; c < val.length(); c++) {
                    if (val.charAt(c) != 'N' && val.charAt(c) != 'E'
                            && val.charAt(c) != 'S' && val.charAt(c) != 'W'
                            && val.charAt(c) != 'U' && val.charAt(c) != 'D') {
                        val = val.replace(val.charAt(c) + "", "");
                        c = -1;
                    }
                }
                ((RoomEdit) rooms[x][y][z]).setLocked(val);
            } else if (field.equalsIgnoreCase("BLOCKED")) {
                val = val.toUpperCase();
                for (int c = 0; c < val.length(); c++) {
                    if (val.charAt(c) != 'N' && val.charAt(c) != 'E'
                            && val.charAt(c) != 'S' && val.charAt(c) != 'W'
                            && val.charAt(c) != 'U' && val.charAt(c) != 'D') {
                        val = val.replace(val.charAt(c) + "", "");
                        c = -1;
                    }
                }
                ((RoomEdit) rooms[x][y][z]).setBlocked(val);
            } else {
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean editRoom(int x, int y, int z, String field, int val) {
        if (rooms[x][y][z] != null) {
            if (field.equalsIgnoreCase("CURRENCY")) {
                ((RoomEdit) rooms[x][y][z]).addCurrencyAmount(val);
            } else if (field.equalsIgnoreCase("SPAWN")) {
                ((RoomEdit) rooms[x][y][z]).setSpawnLevel(val);
            } else if (field.equalsIgnoreCase("REPOP")) {
                ((RoomEdit) rooms[x][y][z]).setRepopTickCount(val);
            } else if (field.equalsIgnoreCase("REMOVEITEM")) {
                ((RoomEdit) rooms[x][y][z]).removeItem(val);
            } else if (field.equalsIgnoreCase("REMOVEMOBILE")) {
                ((RoomEdit) rooms[x][y][z]).removeMobile(val);
            } else if (field.equalsIgnoreCase("REMOVETRIGGER")) {
                ((RoomEdit) rooms[x][y][z]).removeTrigger(val);
            } else {
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean editRoom(int x, int y, int z, String field, boolean val) {
        if (rooms[x][y][z] != null) {
            if (field.equalsIgnoreCase("LOWLIGHT")) {
                ((RoomEdit) rooms[x][y][z]).setLowLight(val);
            } else {
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean deleteRoom(int x, int y, int z) {
        if (rooms[x][y][z] != null) {
            rooms[x][y][z] = null;
            return true;
        } else {
            return false;
        }
    }
    
     @Override
    public boolean moveRoom(int srcX, int srcY, int srcZ, int destX, int destY, int destZ) {
        if (rooms[srcX][srcY][srcZ] != null) {
           if (rooms[destX][destY][destZ] == null) {
               rooms[destX][destY][destZ] = rooms[srcX][srcY][srcZ].cloneThis();
               rooms[srcX][srcY][srcZ] = null;
               
               // Update spawn area if moving spawn area
               if(sx == srcX && sy == srcY && sz == srcZ) {
                   sx = destX;
                   sy = destY;
                   sz = destZ;
               }

                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    @Override
    public void addItemToRoom(Item i, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).addItem(i);
    }

    @Override
    public void addMobileToRoom(Mobile m, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).addMobile(m);
    }

    @Override
    public void addTriggerToRoom(Trigger t, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).addTrigger(t);
    }

    @Override
    public void removeItemFromRoom(int id, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeItem(id);
    }

    @Override
    public void removeMobileFromRoom(int id, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeMobile(id);
    }

    @Override
    public void removeTriggerFromRoom(int id, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeTrigger(id);
    }

    @Override
    public void removeItemFromRoomAtIndex(int index, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeItemAtIndex(index);
    }

    @Override
    public void removeMobileFromRoomAtIndex(int index, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeMobileAtIndex(index);
    }

    @Override
    public void removeTriggerFromRoomAtIndex(int index, int x, int y, int z) {
        ((RoomEdit) rooms[x][y][z]).removeTriggerAtIndex(index);
    }

    @Override
    public String getPaths(int x, int y, int z) {
        String paths = "";

        if (!isBlocked(x, y, z, 'N')) {
            if (rooms[x][y][z].isClosed('N')) {
                if (rooms[x][y][z].isLocked('N')) {
                    paths = paths + ColourUtil.colourise("[NORTH] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[NORTH] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("NORTH ", ColourUtil.open);
            }
        }
        if (!isBlocked(x, y, z, 'E')) {
            if (rooms[x][y][z].isClosed('E')) {
                if (rooms[x][y][z].isLocked('E')) {
                    paths = paths + ColourUtil.colourise("[EAST] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[EAST] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("EAST ", ColourUtil.open);
            }
        }
        if (!isBlocked(x, y, z, 'S')) {
            if (rooms[x][y][z].isClosed('S')) {
                if (rooms[x][y][z].isLocked('S')) {
                    paths = paths + ColourUtil.colourise("[SOUTH] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[SOUTH] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("SOUTH ", ColourUtil.open);
            }
        }
        if (!isBlocked(x, y, z, 'W')) {
            if (rooms[x][y][z].isClosed('W')) {
                if (rooms[x][y][z].isLocked('W')) {
                    paths = paths + ColourUtil.colourise("[WEST] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[WEST] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("WEST ", ColourUtil.open);
            }
        }
        if (!isBlocked(x, y, z, 'U')) {
            if (rooms[x][y][z].isClosed('U')) {
                if (rooms[x][y][z].isLocked('U')) {
                    paths = paths + ColourUtil.colourise("[UP] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[UP] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("UP ", ColourUtil.open);
            }
        }
        if (!isBlocked(x, y, z, 'D')) {
            if (rooms[x][y][z].isClosed('D')) {
                if (rooms[x][y][z].isLocked('D')) {
                    paths = paths + ColourUtil.colourise("[DOWN] ", ColourUtil.locked);
                } else {
                    paths = paths + ColourUtil.colourise("[DOWN] ", ColourUtil.closed);
                }
            } else {
                paths = paths + ColourUtil.colourise("DOWN ", ColourUtil.open);
            }
        }
        return paths.concat(ColourUtil.colourise("", ColourUtil.defaultColour));
    }

    @Override
    public boolean isClosed(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);


        return rooms[x][y][z].isClosed(dir);


    }

    @Override
    public boolean isLocked(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);


        return rooms[x][y][z].isLocked(dir);


    }

    @Override
    public boolean isBlocked(int x, int y, int z, char dir) {

        dir = Character.toUpperCase(dir);

        // Check if room access has been denied manually..
        if (rooms[x][y][z].isBlocked(dir)) {
            return true;
        } // Check if room is outwith the bounds of the map, or not coded yet..
        if (dir == 'N' && (!(y + 1 < maxY) || rooms[x][y + 1][z] == null)) {
            return true;
        } else if (dir == 'E' && (!(x + 1 < maxX) || rooms[x + 1][y][z] == null)) {
            return true;
        } else if (dir == 'S' && (!(y - 1 >= 0) || rooms[x][y - 1][z] == null)) {
            return true;
        } else if (dir == 'W' && (!(x - 1 >= 0) || rooms[x - 1][y][z] == null)) {
            return true;
        } else if (dir == 'U' && (!(z + 1 < maxZ) || rooms[x][y][z + 1] == null)) {
            return true;
        } else if (dir == 'D' && (!(z - 1 >= 0) || rooms[x][y][z - 1] == null)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isLightPresent(int x, int y, int z) {
        System.out.println("Into is light present: rooms[x][y][z] == null?" + (rooms[x][y][z] == null));
        return rooms[x][y][z].lightPresent();
    }

    @Override
    public String getClosed(int x, int y, int z) {
        return ((RoomEdit) rooms[x][y][z]).getClosed();
    }

    @Override
    public String getLocked(int x, int y, int z) {
        return ((RoomEdit) rooms[x][y][z]).getLocked();
    }

    @Override
    public String getBlocked(int x, int y, int z) {
        return ((RoomEdit) rooms[x][y][z]).getBlocked();
    }

    @Override
    public void enterRoom(Player p) {
        System.out.println(rooms[p.x()][p.y()][p.z()] == null);
        rooms[p.x()][p.y()][p.z()].playerEnters(p);
    }

    @Override
    public void leaveRoom(Player p) {
        rooms[p.x()][p.y()][p.z()].playerLeaves(p);
    }

    @Override
    public void movePlayer(Player p, char dir) {
        dir = Character.toUpperCase(dir);

        leaveRoom(p);
        p.move(dir);
        enterRoom(p);
    }

    @Override
    public String tripTriggers(Player p, String input) {
        return rooms[p.x()][p.y()][p.z()].returnTriggerOutput(this, p, input);
    }

    @Override
    public void open(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);
        rooms[x][y][z].open(dir);
    }

    @Override
    public boolean closeableExit(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);

        return rooms[x][y][z].closeableExit(dir);
    }

    @Override
    public void close(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);
        rooms[x][y][z].close(dir);
    }

    @Override
    public void unlock(int x, int y, int z, char dir) {
        dir = Character.toUpperCase(dir);
        rooms[x][y][z].unlock(dir);
    }

    @Override
    public boolean updatePlayerSpawn(Player p) {
        int spawnL = rooms[p.x()][p.y()][p.z()].getSpawnLevel();

        return p.updateSpawn(spawnL);
    }

    @Override
    public boolean roomHasContent(int x, int y, int z) {
        return rooms[x][y][z].hasContent();
    }

    @Override
    public boolean roomHasItem(int x, int y, int z, int val, int quant) {
        return rooms[x][y][z].hasItem(val, quant);
    }

    @Override
    public boolean roomHasMobile(int x, int y, int z, int val, int quant) {
        return rooms[x][y][z].hasMobile(val, quant);
    }

    @Override
    public boolean roomHasTrigger(int x, int y, int z, int val, int quant) {
        return rooms[x][y][z].hasTrigger(val, quant);
    }

    @Override
    public Item takeRoomItem(int x, int y, int z, String item) {
        Item i = rooms[x][y][z].takeItem(item);

        return i;
    }

    @Override
    public int takeRoomCurrency(int x, int y, int z, Player p) {
        int currency = rooms[x][y][z].takeAllCurrency();

        p.modifyCurrencyAmount(currency);

        return currency;
    }

    @Override
    public int takeRoomCurrency(int x, int y, int z, Player p, int quantity) {
        int currency = rooms[x][y][z].takeCurrencyAmount(quantity);

        p.modifyCurrencyAmount(currency);

        return currency;
    }

    @Override
    public int dropRoomCurrency(int x, int y, int z, Player p) {
        int currency = p.getCurrencyAmount();
        rooms[x][y][z].addCurrencyAmount(currency);
        p.modifyCurrencyAmount(-currency);

        return currency;
    }

    @Override
    public int dropRoomCurrency(int x, int y, int z, Player p, int quant) {
        if (p.getCurrencyAmount() >= quant) {
            rooms[x][y][z].addCurrencyAmount(quant);
            p.modifyCurrencyAmount(-quant);

            return quant;
        } else {
            int currency = p.getCurrencyAmount();
            rooms[x][y][z].addCurrencyAmount(currency);
            p.modifyCurrencyAmount(-currency);

            return currency;
        }
    }

    // First item is always currency quantity int, rest items
    @Override
    public Object[] lootRoomContainer(int x, int y, int z, String container) {
        Mobile corpse = rooms[x][y][z].getMobile(container);

        if (corpse.getID() > 0) {
            if (corpse.isDead()) {
                Item i;
                ArrayList<Item> items = new ArrayList<Item>();

                while ((i = corpse.takeInventoryItem(0)).getID() > 0) {
                    items.add(i);
                }

                // First is always currency quantity
                Object[] currencyAndItems = new Object[items.size() + 1];

                currencyAndItems[0] = "" + corpse.takeCurrencyAmount(corpse.getCurrencyContents());

                for (int c = 0; c
                        < items.size(); c++) {
                    currencyAndItems[c + 1] = items.get(c);
                }
                return currencyAndItems;
            } else {
                return new Object[0];
            }
        } else {
            return new Object[0];
        }
    }

    @Override
    public Mobile getRoomMobile(int x, int y, int z, String mobile) {
        return rooms[x][y][z].getMobile(mobile);
    }

    @Override
    public String getRoomMobileName(int x, int y, int z, String mobile) {
        Mobile m = rooms[x][y][z].getMobile(mobile);

        return m.getName();
    }

    @Override
    public Mobile[] getRoomVendors(int x, int y, int z) {
        ArrayList<Mobile> vendors = new ArrayList<Mobile>();

        int c = 0;
        Mobile m;
        while ((m = rooms[x][y][z].getMobileAtPosition(c)).getID() != 0) {
            if (m.isVendor()) {
                vendors.add(m);
            }
            c++;
        }

        Mobile[] vendorsArray = new Mobile[vendors.size()];

        return vendors.toArray(vendorsArray);
    }

    @Override
    public int getRoomPlayer(int x, int y, int z, String player) {
        Player p = rooms[x][y][z].getPlayer(player);

        return p.getID();
    }

    @Override
    public String aggroMobsAttack(Player p) {
        return rooms[p.x()][p.y()][p.z()].aggroMobsAttack(p.getID());
    }

    @Override
    public String[][] aggroGroupsAttack(Player p) {
        return rooms[p.x()][p.y()][p.z()].aggroGroupsAttack(p.getID());
    }

    @Override
    public void stopFighting(Player p) {
        rooms[p.x()][p.y()][p.z()].stopFighting(p);
    }

    @Override
    public String decayCorpses(int x, int y, int z) {
        String output = rooms[x][y][z].decayEmptyCorpses();

        return output;
    }

    @Override
    public String decayTempItems(int x, int y, int z) {
        String output = rooms[x][y][z].decayTempItems();

        return output;
    }

    @Override
    public void aggroMobs(int x, int y, int z) {
        rooms[x][y][z].aggroAllMobs();
    }

    @Override
    public Mobile[] getAggroMobs(int player, int x, int y, int z) {

        Mobile[] mobs = new Mobile[rooms[x][y][z].getAggroMobs(player).size()];

        for (int c = 0; c < mobs.length; c++) {
            mobs[c] = rooms[x][y][z].getAggroMobs(player).get(c);
        }
        return mobs;
    }

    @Override
    public RoomManager cloneThis() {
        RoomManager newClone = new RoomManagerImp();
        try {
            newClone = (RoomManager) this.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return newClone;
    }

    @Override
    public Object clone() {
        try {
            Object clone = super.clone();
            ((RoomManagerImp) clone).rooms = new Room[maxX][maxY][maxZ];
            for (int x = 0; x < maxX; x++) {
                for (int y = 0; y < maxY; y++) {
                    for (int z = 0; z < maxZ; z++) {
                        if (rooms[x][y][z] != null) {
                            ((RoomManagerImp) clone).rooms[x][y][z] = rooms[x][y][z].cloneThis();
                        } else {
                            ((RoomManagerImp) clone).rooms[x][y][z] = null;
                        }
                    }
                }
            }
            return clone;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
